#include <unistd.h>

/* Need this defined for lseek64 */
#define _LARGEFILE64_SOURCE

/* Disk functions */
int  read_blk(int device, int blk, int blk_size, char* buf);
int  write_blk(int device, int blk, int blk_size, char* buf);
int  get_super(int device, SUPER* super);
int  put_super(int device, SUPER* super);
void set_bit  (char buf[MAX_BLOCK_SIZE], int bit);
void clear_bit(char buf[MAX_BLOCK_SIZE], int bit);
int  test_bit (char buf[MAX_BLOCK_SIZE], int bit);
ulong allocate_free_inode(int device);
int   deallocate_inode(int device, ulong inode);
ulong allocate_free_block(int device, int group);
int   deallocate_block(int device, ulong block);

/* Pathname helper functions */
int dir_name(char* path, char outDir[MAX_PATH_LENGTH]);
int base_name(char* path, char outDir[MAX_PATH_LENGTH]);
int tokenize_pathname(char* path, char tokens_out[MAX_PATH_LENGTH], int* num_tokens_out);

/* Ext2/Ext3 Inode functions */
MINODE*	get_inode(int device, ulong inode_num);
int		put_inode(MINODE* mip);
LOC		get_inode_number(char* path);
int		get_inode_name(MINODE* mip, ulong parentNum, char nameOut[EXT2_MAX_FILE_LENGTH]);
ulong	get_inode_parent_number(MINODE* mip);
ulong	search_inode(MINODE* mip, char* name);
void	print_inode_mode(int mode);

/* Extra helpers */
int read_group(int device, int group_num, GROUP* group);
int write_group(int device, int group_num, GROUP* group);
int get_device_info(int device, EXT2_DISK** info_out);
int search_memInode_table(int device, ulong inode_num);
int find_free_memInode(void);
int find_free_mount(void);

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int read_blk(int device, int blk, int blk_size, char* buf)
{
	/* Make sure device exists */
	if (device == -1)
		return -1;
	/* Make sure buf exists */
	if (buf == 0)
		return -1;
	/* Seek to the right area of the device */
	if (lseek64(device, (long long)blk * blk_size, SEEK_SET) == -1)
		return -1;
	/* Read block */
	if (read(device, buf, blk_size) == -1)
		return -1;
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int write_blk(int device, int blk, int blk_size, char* buf)
{
	/* Make sure device exists */
	if (device == -1)
		return -1;
	/* Make sure buf exists */
	if (buf == 0)
		return -1;
	/* Seek to the right area of the device */
	if (lseek64(device, (long long)blk * blk_size, SEEK_SET) == -1)
		return -1;
	/* Read block */
	if (write(device, buf, blk_size) == -1)
		return -1;
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int get_super(int device, SUPER* super)
{
	char buf[STD_BLK_SIZE];
	if (read_blk(device, 1, STD_BLK_SIZE, buf) == -1)
		return -1;
	(*super) = (*((SUPER*)buf));
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int put_super(int device, SUPER* super)
{
	char buf[STD_BLK_SIZE];
	if (read_blk(device, 1, STD_BLK_SIZE, buf) == -1)
		return -1;
	(*((SUPER*)buf)) = (*super);
	if (write_blk(device, 1, STD_BLK_SIZE, buf) == -1)
		return -1;
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
void set_bit(char buf[MAX_BLOCK_SIZE], int bit)
{
	ulong byte = bit / 8;
	ulong offset = bit % 8;
	if (byte < MAX_BLOCK_SIZE)
		buf[byte] |= (1 << offset);
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
void clear_bit(char buf[MAX_BLOCK_SIZE], int bit)
{
	ulong byte = bit / 8;
	ulong offset = bit % 8;
	if (byte < MAX_BLOCK_SIZE)
		buf[byte] &= (~(1 << offset));
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int test_bit(char buf[MAX_BLOCK_SIZE], int bit)
{
	ulong byte = bit / 8;
	ulong offset = bit % 8;	
	if (byte < MAX_BLOCK_SIZE)
		if (buf[byte] & (1 << offset))
			return 1;
	return 0;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
ulong allocate_free_inode(int device)
{
	int i = 0, j = 0, group = 0;
	ulong inode = 1;
	char buf[MAX_BLOCK_SIZE];
	GROUP curGroup;
	EXT2_DISK* diskInfo;
	SUPER super;
	
	if (get_device_info(device, &diskInfo) == -1)
		return 0;
		
	if (get_super(device, &super) == -1)
		return 0;
		
	/* Pick a random group first, then increment from there */
	group = rand() % diskInfo->numGroups;
	inode = (super.s_inodes_per_group * group) + 1;
	for (i = 0; i < diskInfo->numGroups; i++)
	{
		if (group == diskInfo->numGroups)
			group = 0;
		
		if (read_group(device, group, &curGroup) == -1)
			return 0;
			
		if (read_blk(device, curGroup.bg_inode_bitmap, diskInfo->blockSize, buf) == -1)
			return 0;
			
		for (j = 0; j < super.s_inodes_per_group; j++)
		{
			if (test_bit(buf, j) == 0)
			{
				set_bit(buf, j);
				/* Write IMAP back to disk */
				if (write_blk(device, curGroup.bg_inode_bitmap, diskInfo->blockSize, buf) == -1)
					return 0;
				/* Update superblock and group descriptor */
				super.s_free_inodes_count--;
				curGroup.bg_free_inodes_count--;
				/* Write group back to disk */
				if (write_group(device, group, &curGroup) == -1)
					return 0;
				/* Write super back to disk */
				if (put_super(device, &super) == -1)
					return 0;
				return inode;
			}
			inode++;
		}
		group++;
	}
	return 0;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int deallocate_inode(int device, ulong inode)
{
	char buf[MAX_BLOCK_SIZE];
	EXT2_DISK* diskInfo;
	GROUP group;
	SUPER super;
	
	if (get_device_info(device, &diskInfo) == -1)
		return -1;
		
	if (get_super(device, &super) == -1)
		return -1;
	
	if (read_group(device, inode / super.s_inodes_per_group, &group) == -1)
		return -1;
		
	if (read_blk(device, group.bg_inode_bitmap, diskInfo->blockSize, buf) == -1)
		return -1;
		
	clear_bit(buf, (inode - 1) % super.s_inodes_per_group);
	
	if (write_blk(device, group.bg_inode_bitmap, diskInfo->blockSize, buf) == -1)
		return -1;
		
	super.s_free_inodes_count++;
	group.bg_free_inodes_count++;
	
	if (write_group(device, (inode - 1) / super.s_inodes_per_group, &group) == -1)
		return -1;	
	if (put_super(device, &super) == -1)
		return -1;		
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
ulong allocate_free_block(int device, int group)
{
	int i = 0, j = 0;
	ulong block = 0;
	char buf[MAX_BLOCK_SIZE];
	GROUP curGroup;
	EXT2_DISK* diskInfo;
	SUPER super;
	
	if (get_device_info(device, &diskInfo) == -1)
		return 0;
		
	if (get_super(device, &super) == -1)
		return 0;
		
	if (read_group(device, group, &curGroup) == -1)
		return 0;
		
	for (i = 0; ((i < diskInfo->numGroups) && (curGroup.bg_free_blocks_count == 0)); i++)
	{
		if (group == diskInfo->numGroups)
			group == 0;
		else
			group++;
			
		if (read_group(device, group, &curGroup) == -1)
			return 0;
	}
	
	if (read_blk(device, curGroup.bg_block_bitmap, diskInfo->blockSize, buf) == -1)
		return 0;
	
	block = super.s_blocks_per_group * group;
	for (j = 0; j < super.s_blocks_per_group; j++)
	{
		if (test_bit(buf, j) == 0)
		{
			set_bit(buf, j);
			/* Write BMAP back to disk */
			if (write_blk(device, curGroup.bg_block_bitmap, diskInfo->blockSize, buf) == -1)
				return -1;
			/* Update superblock and group descriptor */
			super.s_free_blocks_count--;
			curGroup.bg_free_blocks_count--;
			/* Write group back to disk */
			if (write_group(device, group, &curGroup) == -1)
				return 0;
			/* Write super back to disk */
			if (put_super(device, &super) == -1)
				return 0;
			/* Clear new block to all zeros to avoid garbage */
			memset(buf, 0, MAX_BLOCK_SIZE);
			if (write_blk(device, block, diskInfo->blockSize, buf) == -1)
				return 0;
			return block;
		}
		block++;
	}
	return 0;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int deallocate_block(int device, ulong block)
{
	char buf[MAX_BLOCK_SIZE];
	EXT2_DISK* diskInfo;
	GROUP group;
	SUPER super;
	
	if (get_device_info(device, &diskInfo) == -1)
		return -1;

	if (get_super(device, &super) == -1)
		return -1;

	if (read_group(device, block / super.s_blocks_per_group, &group) == -1)
		return -1;

	if (read_blk(device, group.bg_block_bitmap, diskInfo->blockSize, buf) == -1)
		return -1;

	clear_bit(buf, (block - 1) % super.s_blocks_per_group);
	
	if (write_blk(device, group.bg_block_bitmap, diskInfo->blockSize, buf) == -1)
		return -1;
		
	super.s_free_blocks_count++;
	group.bg_free_blocks_count++;
	
	if (write_group(device, block / super.s_blocks_per_group, &group) == -1)
		return -1;
	if (put_super(device, &super) == -1)
		return -1;
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int dir_name(char* path, char outDir[MAX_PATH_LENGTH])
{
	if (path == 0)
		return -1;
	else
	{
		int newLen = 0;
		char* tmp = strrchr(path, '/');
		newLen = strlen(path) - strlen(tmp) + 1;
		strncpy(outDir, path, newLen);
		outDir[newLen] = '\0';
		return 1;
	}
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int base_name(char* path, char outDir[EXT2_MAX_FILE_LENGTH])
{
	if (path == 0)
		return -1;
	else
	{
		char* tmp1 = strrchr(path, '/');
		strcpy(outDir, tmp1 + 1);
		return 1;
	}
}

/*
 * Function: tokenize_pathname
 * Description: This function makes a copy of "path" in "tokens" and strips
 * 				the first '/' char. It then searches through "tokens"
 * 				and replaces each '/' char with a '\0' char. The number
 * 				of strings in "tokens" is kept in "num_tokens"
 * Returns: N/A
 */
int tokenize_pathname(char* path, char tokens_out[MAX_PATH_LENGTH], int* num_tokens_out)
{
	int i = 0;
	*num_tokens_out = 1;
	strcpy(tokens_out, path + 1);
	for (i = 0; i < strlen(path) - 1; i++)
	{
		if (tokens_out[i] == '/')
		{
			(*num_tokens_out)++;
			tokens_out[i] = '\0';
		}
	}
	if (path[strlen(path) - 1] == '/')
		(*num_tokens_out)--;
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
MINODE*	get_inode(int device, ulong inode_num)
{
	int inodeTableIndex = search_memInode_table(device, inode_num);
	
	if (inodeTableIndex > -1)
	{
		memInodes[inodeTableIndex].refCount++;
		return &(memInodes[inodeTableIndex]);
	}
	else
	{
		int inodeGroupNum, groupInodeNum, inodeOffset, inodeBlkOffset;
		char buf[8192];
		GROUP inodeGroup;
		EXT2_DISK* diskInfo;
		SUPER super;
		
		/* Get appropriate device info */
		if (get_device_info(device, &diskInfo) == -1)
			return 0;
		
		/* Get super block */
		if (get_super(device, &super) == -1)
			return 0;
			
		/* Calculate needed numbers */
		inodeGroupNum = (inode_num - 1) / super.s_inodes_per_group;
		groupInodeNum = inode_num % super.s_inodes_per_group;
		inodeOffset = ((inode_num - 1) % diskInfo->inodesPerBlk) * diskInfo->inodeSize;
		inodeBlkOffset = (groupInodeNum - 1) / diskInfo->inodesPerBlk;
		
		/* Read group the inode is in */
		if (read_group(device, inodeGroupNum, &inodeGroup) == -1)
			return 0;
		
		/* Read block where inode is */
		if (read_blk(device, inodeGroup.bg_inode_table + inodeBlkOffset, diskInfo->blockSize, buf) == -1)
			return 0;
		
		/* Find free MINODE */
		inodeTableIndex = find_free_memInode();
		if (inodeTableIndex == -1)
		{
			printf("The in-memory inode table is full\n\n");
			return 0;
		}
		
		/* Create new entry in memInodes */
		memInodes[inodeTableIndex].inode = (*((INODE*)(buf + inodeOffset)));
		memInodes[inodeTableIndex].location.deviceHandle = device;
		memInodes[inodeTableIndex].location.inodeNum = inode_num;
		memInodes[inodeTableIndex].refCount = 1;
		memInodes[inodeTableIndex].dirty = false;
		memInodes[inodeTableIndex].busy = false;
		memInodes[inodeTableIndex].mounted = false;
		memInodes[inodeTableIndex].mountedDevice = 0;
		
		return &(memInodes[inodeTableIndex]);
	}	
	return 0;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int put_inode(MINODE* mip)
{
	mip->refCount--;
	if ((mip->refCount == 0) && (mip->dirty == true))
	{
		int inodeGroupNum, groupInodeNum, inodeOffset, inodeBlkOffset;
		char buf[8192];
		GROUP inodeGroup;
		EXT2_DISK* diskInfo;
		SUPER super;
		
		/* Get appropriate device info */
		if (get_device_info(mip->location.deviceHandle, &diskInfo) == -1)
			return 0;
			
		/* Get super block */
		if (get_super(mip->location.deviceHandle, &super) == -1)
			return 0;
			
		/* Calculate needed numbers */
		inodeGroupNum = (mip->location.inodeNum - 1) / super.s_inodes_per_group;
		groupInodeNum = mip->location.inodeNum % super.s_inodes_per_group;
		inodeOffset = ((mip->location.inodeNum - 1) % diskInfo->inodesPerBlk) * diskInfo->inodeSize;
		inodeBlkOffset = (groupInodeNum - 1) / diskInfo->inodesPerBlk;
		
		/* Read group the inode is in */
		if (read_group(mip->location.deviceHandle, inodeGroupNum, &inodeGroup) == -1)
			return 0;
					
		/* Read block where inode is */
		if (read_blk(mip->location.deviceHandle, inodeGroup.bg_inode_table + inodeBlkOffset, diskInfo->blockSize, buf) == -1)
			return 0;
		
		/* Write inode to buf */
		(*((INODE*)(buf + inodeOffset))) = mip->inode;
		
		/* Write buf back to disk */
		if (write_blk(mip->location.deviceHandle, inodeGroup.bg_inode_table + inodeBlkOffset, diskInfo->blockSize, buf) == -1)
			return 0;
	}
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
LOC get_inode_number(char* path)
{
	LOC inodeLoc = { mountTable[ROOT_DEVICE].handle, 0 };

	if (path[0] != '/')
		return inodeLoc;
	else if (strcmp(path, "/") == 0)
	{
		inodeLoc.inodeNum = 2;
		return inodeLoc;
	}
	else
	{
		int numTokens = 0, i = 0;
		ulong nextInode = 0;
		char tokens[MAX_PATH_LENGTH], *tokenPtr = 0;
		MINODE* mip = 0;
		
		tokenize_pathname(path, tokens, &numTokens);
		tokenPtr = tokens;
		
		mip = get_inode(inodeLoc.deviceHandle, ROOT_INODE);
		if (mip == 0)
			return inodeLoc;
			
		for (i = 0; i < numTokens; i++)
		{
			if ((mip->mounted == true) && (strcmp(tokenPtr, "..") != 0))
				inodeLoc.deviceHandle = mip->mountedDevice->handle;
			
			nextInode = search_inode(mip, tokenPtr);
			put_inode(mip);
			
			if (nextInode == 0)
				return inodeLoc;
			
			mip = get_inode(inodeLoc.deviceHandle, nextInode);
			if (mip == 0)
				return inodeLoc;
				
			tokenPtr += strlen(tokenPtr) + 1;
		}
		
		if (mip->mounted == true)
		{
			inodeLoc.inodeNum = ROOT_INODE;
			inodeLoc.deviceHandle = mip->mountedDevice->handle;
		}
		else
			inodeLoc.inodeNum = nextInode;
		put_inode(mip);
		return inodeLoc;
	}
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int get_inode_name(MINODE* mip, ulong parentNum, char nameOut[EXT2_MAX_FILE_LENGTH])
{
	if (S_ISDIR(mip->inode.i_mode))
	{
		int i = 0, j = 0;
		char buf[8192], *pnt = 0;
		EXT2_DISK* diskInfo;
		MINODE* parent = 0;
		
		parent = get_inode(mip->location.deviceHandle, parentNum);
		if (parent == 0)
			return -1;
			
		if (get_device_info(mip->location.deviceHandle, &diskInfo) == -1)
			return -1;
		
		for (i = 0; i < NUM_DIRECT_BLOCKS; i++)
		{
			if (parent->inode.i_block[i] != 0)
			{
				if (read_blk(parent->location.deviceHandle, parent->inode.i_block[i], diskInfo->blockSize, buf) == -1)
					return -1;
				
				pnt = buf; j = 0;
				while (j < diskInfo->blockSize)
				{
					if (((DIR*)pnt)->inode == mip->location.inodeNum)
					{
						strncpy(nameOut, ((DIR*)pnt)->name, ((DIR*)pnt)->name_len);
						nameOut[((DIR*)pnt)->name_len] = '\0';
						return 1;
					}
					j += ((DIR*)pnt)->rec_len; pnt += ((DIR*)pnt)->rec_len;
				}
			}
		}
	}
	return -1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
ulong get_inode_parent_number(MINODE* mip)
{
	if (mip == rootInode)
		return ROOT_INODE;
	else
	{
		char buf[8192], *pnt = 0;
		ulong parentNum = 0;
		EXT2_DISK* diskInfo;
		
		if (get_device_info(mip->location.deviceHandle, &diskInfo) == -1)
			return 0;
			
		if (read_blk(mip->location.deviceHandle, mip->inode.i_block[0], diskInfo->blockSize, buf) == -1)
			return 0;
			
		pnt = buf + ((DIR*)buf)->rec_len;
		parentNum = ((DIR*)pnt)->inode;
		return parentNum;
	}	
	return 0;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
ulong search_inode(MINODE* mip, char* name)
{
	int i = 0, j = 0;
	char buf[8192], *pnt = 0, curDirEnt[EXT2_MAX_FILE_LENGTH];
	EXT2_DISK* diskInfo;
	MINODE* inode = mip;
	
	if ((inode->mounted == true) && (strcmp(name, "..") != 0))
	{
		if (get_device_info(inode->mountedDevice->handle, &diskInfo) == -1)
			return 0;
		inode = get_inode(inode->mountedDevice->handle, ROOT_INODE);
	}
	else
		if (get_device_info(inode->location.deviceHandle, &diskInfo) == -1)
			return 0;
			
	//printf("Searching inode %ld on device %d for %s\n", inode->location.inodeNum, inode->location.deviceHandle, name);
	
	for (i = 0; i < NUM_DIRECT_BLOCKS; i++)
	{
		if (inode->inode.i_block[i] != 0)
		{
			if (read_blk(inode->location.deviceHandle, inode->inode.i_block[i], diskInfo->blockSize, buf) == -1)
				return 0;
			
			pnt = buf; j = 0;
			while (j < diskInfo->blockSize)
			{
				memset(curDirEnt, '\0', EXT2_MAX_FILE_LENGTH);
				strncpy(curDirEnt, ((DIR*)pnt)->name, ((DIR*)pnt)->name_len);
				curDirEnt[((DIR*)pnt)->name_len] = '\0';
				//printf("\"%s\" VS \"%s\"\n", name, curDirEnt);
				if (strcmp(name, curDirEnt) == 0)
				{
					ulong inodeNum = ((DIR*)pnt)->inode;
					if ((inode->mounted == true) && (strcmp(name, "..") != 0))
						put_inode(inode);
					return inodeNum;
				}
				j += ((DIR*)pnt)->rec_len; pnt += ((DIR*)pnt)->rec_len;
			}
		}
	}
	if ((inode->mounted == true) && (strcmp(name, "..") != 0))
		put_inode(inode);
	return 0;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
void print_inode_mode(int mode)
{
	if (mode & IMODE_DIRECTORY)
		putchar('d');
	else
		putchar('-');
		
	if (mode & IACCESS_USR_READ)
		putchar('r');
	else
		putchar('-');
	if (mode & IACCESS_USR_WRITE)
		putchar('w');
	else
		putchar('-');
	if (mode & IACCESS_USR_EXEC)
		putchar('x');
	else
		putchar('-');
		
	if (mode & IACCESS_GRP_READ)
		putchar('r');
	else
		putchar('-');
	if (mode & IACCESS_GRP_WRITE)
		putchar('w');
	else
		putchar('-');
	if (mode & IACCESS_GRP_EXEC)
		putchar('x');
	else
		putchar('-');
		
	if (mode & IACCESS_OTH_READ)
		putchar('r');
	else
		putchar('-');
	if (mode & IACCESS_OTH_WRITE)
		putchar('w');
	else
		putchar('-');
	if (mode & IACCESS_OTH_EXEC)
		putchar('x');
	else
		putchar('-');
		
	printf(" ");
}

/*
 * Function: read_group
 * Description: This function takes in a handle to a device and attempts
 * 				to read the group given by "group_num". If successful,
 * 				"group" will contain a copy of the specified group.
 * Returns: -1 upon failure and 1 upon success.
 */
int read_group(int device, int group_num, GROUP* group)
{
	int groupBlk, groupOffset;
	EXT2_DISK* diskInfo;
	char buf[8192];
	SUPER super;
	if (get_device_info(device, &diskInfo) == -1)
		return -1;
	if (get_super(device, &super) == -1)
		return -1;
	if (group_num >= diskInfo->numGroups)
		return -1;
	/* Calculate needed numbers */
	groupBlk = (group_num / diskInfo->groupsPerBlk) + (super.s_first_data_block + 1);
	groupOffset = (group_num % diskInfo->groupsPerBlk) * sizeof(GROUP);
	if (read_blk(device, groupBlk, diskInfo->blockSize, buf) == -1)
		return -1;
	/* Copy the group from the block to "group" */
	memcpy(group, buf + groupOffset, sizeof(GROUP));
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int write_group(int device, int group_num, GROUP* group)
{
	int groupBlk, groupOffset;
	EXT2_DISK* diskInfo;
	char buf[8192];
	SUPER super;
	if (get_device_info(device, &diskInfo) == -1)
		return -1;
	if (get_super(device, &super) == -1)
		return -1;
	if (group_num >= diskInfo->numGroups)
		return -1;
	/* Calculate needed numbers */
	groupBlk = (group_num / diskInfo->groupsPerBlk) + (super.s_first_data_block + 1);
	groupOffset = (group_num % diskInfo->groupsPerBlk) * sizeof(GROUP);
	/* Read block to write to */
	if (read_blk(device, groupBlk, diskInfo->blockSize, buf) == -1)
		return -1;
	/* Write group to buf */
	(*((GROUP*)(buf + groupOffset))) = (*group);
	/* Write buf back to block */
	if (write_blk(device, groupBlk, diskInfo->blockSize, buf) == -1)
		return -1;
	return 1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int get_device_info(int device, EXT2_DISK** info_out)
{
	if (info_out == 0)
		return -1;
	else
	{
		int i;
		for (i = 0; i < NUM_MOUNTS; i++)
		{
			if (mountTable[i].handle == device)
			{
				(*info_out) = &(mountTable[i].diskInfo);
				return 1;
			}
		}
		return -1;
	}
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int search_memInode_table(int device, ulong inode_num)
{
	int i;
	for (i = 0; i < NUM_MEM_INODES; i++)
	{
		if ((memInodes[i].refCount > 0) &&
			(memInodes[i].location.inodeNum == inode_num) &&
			(memInodes[i].location.deviceHandle == device))
			return i;
	}
	return -1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int find_free_memInode(void)
{
	int i;
	for (i = 0; i < NUM_MEM_INODES; i++)
		if (memInodes[i].refCount == 0)
			return i;
	return -1;
}

/*
 * Function: 
 * Description: 
 * Returns: 
 */
int find_free_mount(void)
{
	int i;
	for (i = 0; i < NUM_MOUNTS; i++)
		if (mountTable[i].mountedInode == 0)
			return i;
	return -1;
}
